home *** CD-ROM | disk | FTP | other *** search
/ CICA 1993 April / CICA MS Windows - April 1993.iso / unzipped / programr / bcpp / ddispatc / ddispatc.top
Text File  |  1991-12-05  |  9KB  |  193 lines

  1. How OWL does dynamic dispatch virtual table's (DDVT's).
  2.  
  3. The short version:
  4. We have extended the language so that you can associate an unsigned
  5. int with a virutal function.  An example of the syntax is:
  6.  
  7. class A {
  8. virtual void foo() = [ 12 ];
  9. };
  10.  
  11. When the compiler encounters this syntax, it will generate the virtual
  12. table for class A in a new way.  It will associate the number 12 with
  13. the address of the function A::foo(void).  This is all the compiler does.
  14.  
  15. Within OWL, there are a few WndProc's which have been exported.
  16. When they get a windows message, windows tells it what window this
  17. message is for through the hWnd parameter.  From this OWL figures out
  18. which of the users object's this message is for.  It then scans the
  19. virtual table's of this class, then its base classes looking for a
  20. function which has been associated with that message. It then
  21. calls that function directly, passing it a RTMessage ( referenence to
  22. a TMessage structure), which describes the message.
  23.  
  24. So most of the work for dynamic dispatching is done by OWL.
  25.  
  26. The long version:
  27. When you go
  28. class A {
  29. public:
  30.     virtual void foo() = [ 12 ];
  31. };
  32. where 12 integer (2 byte expression), and the function foo is virtual,
  33. foo is now called a dynamically dispatched virtual function.
  34.  
  35. To understand the format of the virtual function table, lets take
  36. a more complicated example.
  37.  
  38. class A {
  39.   virtual void dfoo() = [2];
  40.   virtual void nfoo();
  41.   virtual void dfoo2() = [3];
  42. };
  43.  
  44. Assuming default settings for virtual tables, (ie smart and not far)
  45. The compiler will generate a virtual function table called @@A@
  46. withing a virtual segment @A@ within the _DATA segment.  (Virtual
  47. segments are discuessed in the Turbo Assembler manuals).
  48. Here is a commented version of what it will look like:
  49.  
  50. _DATA   segment word public 'DATA'
  51. @A@ segment virtual
  52. @@A@    label   byte        // name of VTable for class A
  53.     dd  @A@dfoo$qv          // list of all dispatched functions, they are
  54.     dd  @A@dfoo2$qv         // always far addresses, despite memory model.
  55.                      // the dispatched functions are listed in the
  56.                      // order they are declared.
  57.     db  2      // following the dispatched functions are the unsigned ints
  58.     db  0      // they're dispatched to.  These int's are listed in the
  59.     db  3      // order of the dispatched functions also.
  60.     db    0
  61.                // after the dispatch numbers comes a
  62.     db  2      // count of how manyu dispatched functions
  63.     db  0      // there are.
  64.  
  65.     db  0      // Following the count is the address of the normal
  66.     db  0      // part of A's first base class'es virtual table.  In this
  67.                // case it is null since A has no base class.
  68.  
  69.     dw    @A@nfoo$qv
  70.         // Finally comes the addresses of the regular virtual functions.
  71.         // This part looks like a normal VTable.
  72. @A@    ends
  73. _DATA    ends
  74.  
  75. The attribute of being virtually dispatched is inheritted.  In the
  76. following,
  77. class B : public A {
  78.      dfoo();
  79. };
  80. dfoo is virtually dispatched, since it was in A.  B's Vtable would like,
  81.  
  82. @@B@    label    byte
  83.     dd  @B@dfoo$qv    // B's dispatched functions,
  84.     db  2             // dispatch numbers
  85.     db    0
  86.     db  1             // count of how many dispatched functions it has.
  87.     db    0
  88.     dw  @@A@+16       // address of where A's Vtable ends.  This will be
  89.         // the start of where the regular VTable is.
  90.     dw    @A@nfoo$qv
  91.     dw    @A@nfoo2$qv
  92.  
  93. The pointer to the base class VTable is how Owl finds out what messages
  94. instances of class B have inherited Dynamic Dispatch functions for.
  95.  
  96. You have to be careful about mixing multiple inheritence with dynamic
  97. dispatching.  The VDDT for a given class only stores a pointer to the
  98. immediate base classes virtual table.  So  given,
  99.  
  100. class C : A, B { ... }
  101.  
  102. OWL will only be able to use dispatched functions from A and C, not B.
  103. All other aspects of multiple inheritence will function the same.
  104.  
  105. How Owl dispatches the message:
  106. When TWindow objects are created, there addresses are stored in an
  107. Association whose key is hwnd's (window handles).  So when  StdWndProc
  108. gets a message, (StdWndProc is the standard window procedure for
  109. OWL objects, it processes most of the messages an OWL app receives), it
  110. uses the hwnd parameter to look up the address, or this pointer of the
  111. TWindow object that this message is for.
  112. Before it scans the DDVT's for a function to call, it does a little
  113. checking on the message number to find functions dispatches to child
  114. ID's or menu commands.  For example, if the message is WM_COMMAND,
  115. it adds wParam ( which identifes the actual command selected ) to
  116. CM_FIRST and then searches for that number in the DDVT's.
  117.  
  118. Searching happens in this way, given the address the TWindow object, OWL
  119. assumes the first thing at that address will be the pointer to the VTable
  120. pointer for that object.   Normally, when the compiler lays out the
  121. physical memory for a class, all the data for the class comes first,
  122. then the Vtable pointer.  However, devired classes must have the Vtable
  123. pointer in the same location.  So given,
  124.  
  125. class A {           in memory                  a
  126. int a, b;           an A object                b
  127. virutal foo();      would look like:           vptr
  128. };
  129.  
  130. class B : A {       in memory                   a
  131. int c;              a B object                  b
  132. virtual foo2();     would look like:            vptr
  133. };                                              c
  134.  
  135. The reason why OWL can assume a vtable pointer at the start of an
  136. object is because all OWL objects derive first from Object as their
  137. base class.  Object is an abstract base class with no data (save a
  138. static member, which is not stored in instances of the class) but
  139. several virtual functions.  Thus the location of the Vtable pointer
  140. for an Object and all its derived is at the start of the class
  141. instance.  Given the this pointer for an instance of a class, one
  142. one could get the vtable pointer by these expressions:
  143.  
  144. #define offsetofVTptr( x ) sizeof( x ) - sizeof( void * )
  145. char *VTBPtr = *( char **)( (char *)this + offsetofVTptr( classname ));
  146.  
  147. This assumes the vtable pointer is of the default size for the
  148. memory model, and that classname is first class in a single
  149. inheritence hierarchy having virtual functions, and that what
  150. this points to is dervived from that class.
  151.  
  152. Actually, the VTable pointer does not point at the start of
  153. the VTable, but the start of the normal part of the VTable.
  154. In our example above, this would be @@A@+16.  After Owl has the address
  155. of the normal part of the vtable, it passes this, and the message its
  156. searching for, to the assembly function Ddispatch to return the
  157. address of the function a message is dispatched to. The way Ddispatch
  158. scans, is that it reads backwards from the vtable address given,
  159. skiping the address for the base class vtable for now, and reads
  160. the count of the number of dispatched functions, and scans backwards
  161. that many words in the VTable looking to see if this object has a
  162. function dispacted to that message.  If so it returns the address
  163. of that function, if not it repeats this for the base class.  If
  164. there is no function dispatched to this message, it returns null.
  165. OWL then proceeds directly to doing default message processing for
  166. the message.   Once Owl has an address for a dispatches function, it
  167. calls it directly after pushing the this pointer and an RTMessage on
  168. the stack.  (so no matter what you declare a dispatched function to
  169. take, in OWL, it allows gets an RTMessage ).
  170.  
  171. You cannot call a dispatched function directly, the virtual function
  172. mechanism is not supported for them.  The code needed to support the
  173. virtual function mechanism given the vtable format would be very
  174. complicated.   Since the vtable pointer points immediatly past
  175. the dispatched functions, the compiler cannot index off it like it
  176. normally does.  Also the addresses of dispatched functions from the base
  177. which are not overidden in the derived class are not stored in the
  178. derived class'es vtable ).
  179.  
  180. There is a case where the compiler will generate a call to a dispatched
  181. function, here is an example of that:
  182.  
  183. class A { public: virtual foo() = [ 2 ]; };
  184. A aa;
  185. main() { aa.foo() }
  186.  
  187. Here, the function is defined in class A, and is not being called through
  188. a reference, so the compiler knows exactly which object is
  189. invoking it.  In this case the compiler calls foo like it would a non
  190. virtual function, by pushing the address of aa on the stack and calling
  191. foo directly.
  192.  
  193.